package licode.unisop.oidc.authsa.service;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import licode.unisop.oidc.auth.modal.AuthorContext;
import licode.unisop.oidc.auth.modal.OidcToken;
import licode.unisop.oidc.auth.modal.SsoToken;
import licode.unisop.oidc.auth.utils.StringTool;
import licode.unisop.oidc.authsa.stp.SaAuthUtil;
import licode.unisop.oidc.authsa.stp.SaTokenSign;
import licode.unisop.oidc.authsa.utils.TokenUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.List;

/**
 * @author licode
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class SaSessionProxy extends SaSession {
    private static final long serialVersionUID = 1023567568900990L;

    /**
     * 用户ID
     */
    private String userId;

    /**
     * 单点登录的会话令牌
     */
    private SsoToken ssoToken;

    /**
     * 会话状态（0：正常；1：冻结）
     */
    private int sessionState;

    public void updateSessionTimeout(long timeout, boolean updateSso) {
        this.updateSessionMinTimeout(timeout, updateSso);
        if (updateSso && null != ssoToken && null != ssoToken.getSsoToken()) {
            long tokenTimeout= System.currentTimeMillis() / 1000 + timeout;
            if (ssoToken.getTimeout() < tokenTimeout) {
                ssoToken.setTimeout(timeout);
                SaAuthUtil.getStpLogic().saveSsoTokenToIdMapping(ssoToken.getSsoToken(), getLoginId(), timeout);
            }
        }
    }

    public void updateSessionMinTimeout(long minTimeout, boolean updateSso) {
        long min = trans(minTimeout);
        long curr = trans(getTimeout());
        if(curr < min) {
            SaManager.getSaTokenDao().updateSessionTimeout(this.getId(), minTimeout);
        }
    }

    public SaSessionProxy(String sessionId) {
        super(sessionId);
        this.sessionState = 0;
    }

    public int calcSsoCount() {
        int count = 0;
        List<TokenSign> tokenList = this.tokenSignListCopy();

        for (TokenSign item : tokenList) {
            SaTokenSign tokenSign = (SaTokenSign) item;
            if (StringTool.isNotBlank(tokenSign.getSsoToken())) {
                ++count;
            }
        }
        return count;
    }

    public boolean hasSsoLogin() {
        return (null != ssoToken && StringTool.isNotBlank(ssoToken.getSsoToken()) && calcSsoCount() > 0);
    }

    public boolean isSsoToken(String token) {
        return ssoToken != null && ssoToken.getSsoToken() != null && ssoToken.getSsoToken().equals(token);
    }

    public void clearSsoToken() {
        this.ssoToken = null;
    }

    public void assignSsoToken(String token, long t) {
        if (null == token) {
            this.ssoToken = null;
        } else {
            this.ssoToken = new SsoToken(token, t);
        }
    }

    public void refreshLogin(String oldToken, SaTokenSign tokenSign) {
        TokenSign oldTokenSign = getTokenSign(oldToken);
        if (null != oldTokenSign) {
            getTokenSignList().remove(oldTokenSign);
        }
        AuthorContext context = SaAuthUtil.loginContextFromAccessToken(tokenSign.getValue());
        long tokenTimeout = context.getRe() + 30;

        getTokenSignList().add(tokenSign);

        // 会话时间往后延迟10秒
        this.updateSessionTimeout(tokenTimeout, isSsoToken(tokenSign.getSsoToken()));
        this.update();
    }

    public OidcToken tokenInfoFromAccessToken(String accessToken) {
        AuthorContext context;
        context = SaAuthUtil.loginContextFromAccessToken(accessToken);
        return TokenUtil.fromTokenSign(accessToken, context);
    }

    public OidcToken tokenInfoFromRefreshToken(String refreshToken) {
        SaTokenSign tokenSign;
        OidcToken tokenInfo = null;

        tokenSign = tokenSignFromRefreshToken(refreshToken);
        if (null != tokenSign) {
            AuthorContext context;
            context = SaAuthUtil.loginContextFromAccessToken(tokenSign.getValue());
            tokenInfo = TokenUtil.fromTokenSign(tokenSign.getValue(), context);
        }

        return tokenInfo;
    }

    public SaTokenSign tokenSignFromRefreshToken(String refreshToken) {
        SaTokenSign tokenSign = null;
        List<TokenSign> tokenList = this.tokenSignListCopy();

        for (TokenSign item : tokenList) {
            SaTokenSign saToken = (SaTokenSign) item;
            if (saToken.getRefreshToken().equals(refreshToken)) {
                tokenSign = saToken;
                break;
            }
        }

        return tokenSign;
    }

    public SaTokenSign findTokenSign(AuthorContext context) {
        SaTokenSign tokenSign = null;
        List<TokenSign> tokenList = this.tokenSignListCopy();

        for (TokenSign item : tokenList) {
            SaTokenSign saToken = (SaTokenSign) item;
            AuthorContext saCtx = SaAuthUtil.loginContextFromAccessToken(item.getValue());

            if (saCtx != null && saCtx.getRid().equals(context.getRid()) &&
                    saCtx.getUid().equals(context.getUid()) &&
                    saCtx.getPid().equals(context.getPid())) {
                tokenSign = saToken;
                break;
            }
        }

        return tokenSign;
    }

    public boolean removeTokenSign(TokenSign tokenSign, boolean needUpdate) {
        if (getTokenSignList().remove(tokenSign)) {
            if (needUpdate) {
                this.update();
            }
            return true;
        } else {
            return false;
        }
    }
}
